﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;
using NativeCode.Core.Data;

namespace NativeCode.Web.Controls
{
	public abstract class GenericDataBoundControl<TDataItem, TLambdaQueryType>
		: DataBoundControl,
		  ILambdaDataSourceProvider<TLambdaQueryType, TDataItem>,
		  INamingContainer
		where TDataItem : new()
		where TLambdaQueryType : LambdaQuery, new()
	{
		protected GenericDataBoundControl()
		{
			CssNames = new List<string>();
		}

		#region Properties

		[Browsable(true)]
		public string ObjectType { get; set; }

		[Browsable(false)]
		public Type DataItemType
		{
			get { return typeof (TDataItem); }
		}

		[Browsable(true)]
		[DefaultValue(false)]
		public bool AutoPostBack { get; set; }

		[Browsable(false)]
		public List<string> CssNames { get; private set; }

		[Browsable(true)]
		[DefaultValue("")]
		public override string CssClass
		{
			get { return string.Join(" ", CssNames.ToArray()); }
			set
			{
				if (!string.IsNullOrEmpty(value))
				{
					CssNames.Clear();
					CssNames.AddRange(value.Split(' '));
				}

				base.CssClass = value;
			}
		}

		#endregion

		#region Control Overrides

		private void OnDataSourceViewSelectCallback(IEnumerable retrievedData)
		{
			// Call OnDataBinding only if it has not already been 
			// called in the PerformSelect method.
			if (IsBoundUsingDataSourceID)
			{
				OnDataBinding(EventArgs.Empty);
			}

			// The PerformDataBinding method binds the data in the  
			// retrievedData collection to elements of the data-bound control.
			PerformDataBinding(retrievedData);
		}

		protected override void PerformSelect()
		{
			// Call OnDataBinding here if bound to a data source using the
			// DataSource property (instead of a DataSourceID), because the
			// databinding statement is evaluated before the call to GetData.       
			if (!IsBoundUsingDataSourceID)
			{
				OnDataBinding(EventArgs.Empty);
			}

			// The GetData method retrieves the DataSourceView object from  
			// the IDataSource associated with the data-bound control.            
			GetData().Select(CreateDataSourceSelectArguments(), OnDataSourceViewSelectCallback);

			// The PerformDataBinding method has completed.
			RequiresDataBinding = false;
			MarkAsDataBound();

			// Raise the DataBound event.
			OnDataBound(EventArgs.Empty);
		}

		#endregion

		#region Data-Binding

		public abstract void PerformDataBinding(IQueryable<TDataItem> results, int resultsCount);

		/// <summary>
		/// You should prefer to overide the abstract method instead.
		/// </summary>
		/// <param name="data"></param>
		protected override void PerformDataBinding(IEnumerable data)
		{
			if (DataSourceLambda != null)
			{
				PerformDataBinding(DataSourceLambda(CreateQueryType()), DataSourceLambdaCount());
			}

			base.PerformDataBinding(data);
		}

		/// <summary>
		/// Override this if you need to call a constructor with parameters or need to fill-out
		/// properties on the query.
		/// </summary>
		/// <returns></returns>
		protected virtual TLambdaQueryType CreateQueryType()
		{
			return new TLambdaQueryType();
		}

		#endregion

		#region Implementation of ILambdaDataSourceProvider<TLambdaQueryType, TDataItem>

		public Func<TLambdaQueryType, IQueryable<TDataItem>> DataSourceLambda { get; set; }

		public Func<int> DataSourceLambdaCount { get; set; }

		public void Bind(Func<TLambdaQueryType, IQueryable<TDataItem>> query, Func<int> queryCount)
		{
			DataSourceLambda = query;
			DataSourceLambdaCount = queryCount;
		}

		#endregion
	}
}